#include<stdio.h>
#include<stdlib.h>
#include<string.h>

#include <openssl/rsa.h>
#include <openssl/pem.h>
#include <openssl/err.h>

/*************************************************************************
功能 : RSA公钥加密
参数 : char *pszIn  输入的明文
参数 : char *inLen  明文的长度
参数 : char *pszPubKey  公钥
返回 : 输出的密文
 *************************************************************************/
char * rsa_pubkey_encrypt(char *pszIn, int inLen, char *pszPubKey)
{
    char *pszOut = NULL;   //输出的密文，需要在外面释放
    RSA *pPubKey = NULL;   //公钥
    BIO *pBio = NULL;

    //PKCS8格式的秘钥
    char szKey[2048] = {0};
    strcat(szKey, "——BEGIN PUBLIC KEY——\n");
    int i, iLen;
    for (i = 0; i < strlen(pszPubKey); i += 64)
    {
        iLen = (strlen(pszPubKey) - i) < 64 ? strlen(pszPubKey) - i : 64;
        strncat(szKey, &pszPubKey[i], iLen);
        strcat(szKey, "\n");
    }
    strcat(szKey, "——END PUBLIC KEY——\n");
    //printf("szKey = %s", szKey);

    if ((pBio = BIO_new_mem_buf((void *) szKey, -1)) == NULL)
    {
        //PrintLog(LOG_ERROR, "BIO_new_mem_buf publicKey error.");
        goto FreeAndExit;
    }

    if ((pPubKey = PEM_read_bio_RSA_PUBKEY(pBio, NULL, 0, NULL)) == NULL)
    {
        //PrintLog(LOG_ERROR, "PEM_read_bio_PUBKEY error.");
        goto FreeAndExit;
    }

    int pdBlock = RSA_size(pPubKey) - 11;
    unsigned int eCount = (inLen / pdBlock) + 1;
    //分组加密
    char *pszEnc = (unsigned char *) malloc(eCount * RSA_size(pPubKey) + 1);
    memset(pszEnc, 0, eCount * RSA_size(pPubKey) + 1);
    int encLen = 0;
    for (int i = 0; i < eCount; i++)
    {
        int ret = RSA_public_encrypt(inLen > pdBlock ? pdBlock : inLen, pszIn, pszEnc + encLen, pPubKey, RSA_PKCS1_PADDING);
        if (ret < 0)
        {
            //PrintLog(LOG_ERROR, "rsa_pub_encrypt RSA_public_encrypt error[%s].", openssl_last_error());
            goto FreeAndExit;
        }
        pszIn += inLen > pdBlock ? pdBlock : inLen;
        encLen += ret;
        inLen -= pdBlock;
    }

    //BASE64编码
    pszOut = (unsigned char *) malloc(eCount * RSA_size(pPubKey) * 2);
    memset(pszOut, 0, eCount * RSA_size(pPubKey) * 2);
    EVP_EncodeBlock((unsigned char*) pszOut, (const unsigned char*) pszEnc, encLen);

FreeAndExit:
    if (pszEnc) free(pszEnc);
    if (pPubKey) RSA_free(pPubKey);
    if (pBio) BIO_free_all(pBio);
    //非线程安全 CRYPTO_cleanup_all_ex_data();

    return pszOut;
}

/*************************************************************************
功能 : RSA公钥解密
参数 : char *pszIn  输入的密文
返回 : 输出的明文
 *************************************************************************/
char * rsa_pub_decrypt(char *pszIn)
{
    char *pszOut = NULL;    //输出的明文，需要在外面释放
    char *pszEnc = NULL;    //加密的密文
    RSA *pPubKey = NULL;    //公钥
    char szFile[256] = {0};
    FILE *file = NULL;

    strcat(szFile, gstruSysParam->szConfigPath);
    strcat(szFile, RSAKEY_PUB);
    if ((file = fopen(szFile, "r")) == NULL)
    {
        //PrintLog(LOG_ERROR, "rsa_pub_decrypt open pub_key file[%s] error.", szFile);
        goto FreeAndExit;
    }
    if ((pPubKey = PEM_read_RSA_PUBKEY(file, NULL, NULL, NULL)) == NULL)
    {
        //PrintLog(LOG_ERROR, "rsa_pub_decrypt PEM_read_RSA_PUBKEY error.");
        goto FreeAndExit;
    }

    //BASE64解码
    pszEnc = (unsigned char *) malloc(strlen(pszIn));
    memset(pszEnc, 0, strlen(pszIn));
    unsigned long inLen = EVP_DecodeBlock((unsigned char*) pszEnc, (const unsigned char*) pszIn, strlen(pszIn));

    int pdBlock = RSA_size(pPubKey);
    unsigned int dCount = inLen / pdBlock;
    //分组解密
    pszOut = (unsigned char *) malloc(inLen + 1);
    memset(pszOut, 0, inLen + 1);
    int lenEnc = 0, lenOut = 0;
    for (int i = 0; i < dCount; i++)
    {
        int ret = RSA_public_decrypt(pdBlock, pszEnc + lenEnc, pszOut + lenOut, pPubKey, RSA_PKCS1_PADDING);
        if (ret < 0)
        {
            //PrintLog(LOG_ERROR, "rsa_pub_decrypt RSA_public_decrypt error[%s].", openssl_last_error());
            if (pszOut) free(pszOut);
            pszOut = NULL;
            goto FreeAndExit;
        }
        lenEnc += pdBlock;
        lenOut += ret;
    }

FreeAndExit:
    if (file) fclose(file);
    if (pszEnc) free(pszEnc);
    if (pPubKey) RSA_free(pPubKey);
    //非线程安全 CRYPTO_cleanup_all_ex_data();

    return pszOut;
}

/*************************************************************************
功能 : RSA私钥加密
参数 : char *pszIn  输入的明文
返回 : 输出的密文
 *************************************************************************/
char * rsa_pri_encrypt(char *pszIn)
{
    char *pszOut = NULL;   //输出的密文，需要在外面释放
    RSA *pPriKey = NULL;   //私钥
    char szFile[256] = {0};
    FILE *file = NULL;

    strcat(szFile, gstruSysParam->szConfigPath);
    strcat(szFile, RSAKEY_PRI);
    if ((file = fopen(szFile, "r")) == NULL)
    {
        //PrintLog(LOG_ERROR, "rsa_pri_encrypt open pub_key file[%s] error.", szFile);
        goto FreeAndExit;
    }
    if ((pPriKey = PEM_read_RSAPrivateKey(file, NULL, NULL, NULL)) == NULL)
    {
        //PrintLog(LOG_ERROR, "rsa_pri_encrypt PEM_read_RSA_PUBKEY error.");
        goto FreeAndExit;
    }

    unsigned long inLen = strlen(pszIn);
    int pdBlock = RSA_size(pPriKey) - 11;
    unsigned int eCount = (inLen / pdBlock) + 1;
    //分组加密
    char *pszEnc = (unsigned char *) malloc(eCount * RSA_size(pPriKey) + 1);
    memset(pszEnc, 0, eCount * RSA_size(pPriKey) + 1);
    int encLen = 0;
    for (int i = 0; i < eCount; i++)
    {
        int ret = RSA_private_encrypt(inLen > pdBlock ? pdBlock : inLen, pszIn, pszEnc + encLen, pPriKey, RSA_PKCS1_PADDING);
        if (ret < 0)
        {
            //PrintLog(LOG_ERROR, "rsa_pri_encrypt RSA_private_encrypt error[%s].", openssl_last_error());
            goto FreeAndExit;
        }
        pszIn += inLen > pdBlock ? pdBlock : inLen;
        encLen += ret;
        inLen -= pdBlock;
    }

    //BASE64编码
    pszOut = (unsigned char *) malloc(eCount * RSA_size(pPriKey) * 2);
    memset(pszOut, 0, eCount * RSA_size(pPriKey) * 2);
    EVP_EncodeBlock((unsigned char*) pszOut, (const unsigned char*) pszEnc, encLen);

FreeAndExit:
    if (file) fclose(file);
    if (pszEnc) free(pszEnc);
    if (pPriKey) RSA_free(pPriKey);
    //非线程安全 CRYPTO_cleanup_all_ex_data();

    return pszOut;
}

/*************************************************************************
功能 : RSA私钥解密
参数 : char *pszIn  输入的密文
参数 : char *inLen  密文的长度
参数 : char *pszPubKey  私钥
返回 : 输出的密文
 *************************************************************************/
char * rsa_prikey_decrypt(char *pszIn, int inLen, char *pszPriKey)
{
    char *pszOut = NULL;   //输出的明文，需要在外面释放
    char *pszEnc = NULL;
    RSA *pPriKey = NULL;   //私钥
    BIO *pBio = NULL;

    //PKCS8格式的秘钥
    char szKey[2048] = {0};
    strcat(szKey, "——BEGIN PRIVATE KEY——\n");
    int i, iLen;
    for (i = 0; i < strlen(pszPriKey); i += 64)
    {
        iLen = (strlen(pszPriKey) - i) < 64 ? strlen(pszPriKey) - i : 64;
        strncat(szKey, &pszPriKey[i], iLen);
        strcat(szKey, "\n");
    }
    strcat(szKey, "——END PRIVATE KEY——\n");
    //printf("szKey = %s", szKey);

    if ((pBio = BIO_new_mem_buf((void *) szKey, -1)) == NULL)
    {
        //PrintLog(LOG_ERROR, "BIO_new_mem_buf privateKey error.");
        goto FreeAndExit;
    }

    if ((pPriKey = PEM_read_bio_RSAPrivateKey(pBio, NULL, NULL, NULL)) == NULL)
    {
        //PrintLog(LOG_ERROR, "PEM_read_bio_RSAPrivateKey error.");
        goto FreeAndExit;
    }

    //BASE64解码
    pszEnc = (unsigned char *) malloc(inLen);
    memset(pszEnc, 0, inLen);
    unsigned long iDataLen = EVP_DecodeBlock((unsigned char*) pszEnc, (const unsigned char*) pszIn, strlen(pszIn));

    int pdBlock = RSA_size(pPriKey);
    unsigned int dCount = iDataLen / pdBlock;
    //分组解密
    pszOut = (unsigned char *) malloc(iDataLen + 1);
    memset(pszOut, 0, iDataLen + 1);
    int lenEnc = 0, lenOut = 0;
    for (int i = 0; i < dCount; i++)
    {
        int ret = RSA_private_decrypt(pdBlock, pszEnc + lenEnc, pszOut + lenOut, pPriKey, RSA_PKCS1_PADDING);
        if (ret < 0)
        {
            //PrintLog(LOG_ERROR, "rsa_pri_decrypt RSA_private_decrypt error[%s].", openssl_last_error());
            if (pszOut) free(pszOut);
            pszOut = NULL;
            goto FreeAndExit;
        }
        lenEnc += pdBlock;
        lenOut += ret;
    }

FreeAndExit:
    if (pszEnc) free(pszEnc);
    if (pPriKey) RSA_free(pPriKey);
    if (pBio) BIO_free_all(pBio);
    //非线程安全 CRYPTO_cleanup_all_ex_data();

    return pszOut;
}
